#ifndef OPERATION_BOX_H_JM_20120627
#define OPERATION_BOX_H_JM_20120627

// ## C++
#include <iostream>
// ##
#include "../RootBox.h"
#include "OperationBoxDescriptor.h"

namespace jm4bbx {

	/**
	 * Sablona pre rodicovsku triedu pre aritmeticke operacie nad dvomi vektormi
	 * hodnot. Vygenerovana trieda obsahuje veskeru funkcionalitu a odvodene 
	 * triedy musia len definovat premennu BOX_ID, ktora identifikuje 
	 * box v modele vypoctu a doplnit konstruktor, ktory reflektuje vsetky 
	 * vyzadovane parametre vytvarania.
	 * 
	 * Za registraciu boxu do factory pre runtime je zodpovedna odvodena trieda.
	 * 
	 * @tparam OperationTrait  Zasada OperationTrait urcuje specificke 
	 * vlastnosti operacie. Zasadou operacie je trieda splnujuca kontrakt, ktory 
	 * vyzaduje definiciu typu politiky datoveho typu operacie a definiciu 
	 * vykonania operacie. Poziadavky demonstruje nasledujuci priklad:
	 * @code
	 *   // Zasada  pre scitanie celych cisel.
	 *  struct IntAddTraitExample {
	 *  
	 * 	  // Typ dat vstupujucich do boxu.
	 * 	  typedef bobox::int_policy operation_policy;
	 * 	
	 * 	   // Vykonanie operacie.
	 * 	   int performeOperation(int x, int y) const { 
	 * 	     return x + y; 
	 * 	   }
	 *  };
	 * @endcode
	 * @tparam RealBoxType Model realne pouzivaneho boxu - tj. odvodeneho boxu. 
	 **/
	template<class OperationTrait/*, class BoxDescriptor*/>
	class OperationBox : public Box/*<BoxDescriptor>*/, public OperationTrait {

	public:
		typedef OperationBoxDescriptor BoxIO;
		//static_assert(
		//	std::is_base_of<OperationBoxDescriptor, BoxDescriptor>::value,
		//	"Parameter sablony (deskriptor boxu) musi byt potomok triedy InputBoxDescriptor!");
		/**
		 * Model konecneho boxu pouzivany v modele vypoctu pre tvorbu boxov.
		 **/
		//typedef  RealBoxModel BoxModel;

		/**
		 * Konstruktor pouzivany z factory na boxy pre runtime.
		 ***/
		OperationBox(bobox::box_id_type id, bobox::box_tid_type tid, 
			bobox::request* req, const bobox::parameters& params) 
			: Box(id, tid, req, params) 
			//, OUT_ARC(bobox::outarc_index_type(0)) 
		{ }

		virtual void initImpl() {

				add_group(BoxIO::INPUT_GROUP, BoxIO::INPUT_COLUMN_1, 
					get_input_descriptors(BoxIO::IN_ARC));

				add_group(BoxIO::OUTPUT_GROUP, BoxIO::OUTPUT_COLUMN, 
					get_output_descriptors(BoxIO::OUT_ARC));
	
				wait4Receive();
		}
		

		virtual void sync_mach_etwas() {

			Group ig = get_group(BoxIO::INPUT_GROUP);
			if (ig.isPoisoned()/*poisoned_*/) {
				send_poisoned(BoxIO::OUT_ARC);
				return;
			}

			const int rpos = ig.getReadPosition();//rdpos_;
			const int rend = ig.getReadEnd();//rdend_;

			//for (; ig.rdpos_ < ig.rdend_; ++ig.rdpos_) {
			for (; ig.getReadPosition() < ig.getReadEnd(); ig.moveReadPosition()) {
				get_data<operation_policy>(BoxIO::OUTPUT_COLUMN)[ig.getReadPosition()/*rdpos_*/] = 
						performeOperation(
							get_data<operation_policy>(BoxIO::INPUT_COLUMN_1)[ig.getReadPosition()/*rdpos_*/],
				    		get_data<operation_policy>(BoxIO::INPUT_COLUMN_2)[ig.getReadPosition()/*rdpos_*/]
				);

			}

			Group og = get_group(BoxIO::OUTPUT_GROUP);

			og.setWriteBegin(rpos);//wrbegin_ = rpos;
			og.setWritePosition(rend);//wrpos_ = rend;
			og.setWriteEnd(rend);//wrend_ = rend;

			send(BoxIO::OUT_ARC, BoxIO::OUTPUT_GROUP);

			wait4Receive();

		}
		
	private:
		void wait4Receive() {
			receive(BoxIO::IN_ARC, BoxIO::INPUT_GROUP);
		}

	};

} // namespace jm4bbx



#endif
